package com.xiefengf.elasticearch_template;

import com.xiefengf.es.Product;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedHistogram;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.ParsedAvg;
import org.elasticsearch.search.aggregations.metrics.ParsedValueCount;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.*;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.elasticsearch.core.query.highlight.Highlight;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightField;
import org.springframework.data.elasticsearch.core.query.highlight.HighlightParameters;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author xiefengf
 * @version 1.0
 * @date 2024/3/27 23:05
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class EsTest_ElasticsearchRestTemplate {

    @Resource
    private ElasticsearchRestTemplate elasticsearchTemplate;

    /**
     * 创建索引
     */
    @Test
    public void createIndex() {
        // 索引操作器
        IndexOperations indexOps = elasticsearchTemplate.indexOps(Product.class);
        // 是否存在，存在则先删除
        if (indexOps.exists()) {
            indexOps.delete();
        }
        // 创建索引
        boolean indexBool = indexOps.create();
        // 根据实体类获取映射关系
        Document mapping = indexOps.createMapping(Product.class);
        // 将映射关系添加至索引中
        boolean mappingBool = indexOps.putMapping(mapping);
        System.out.println(indexBool && mappingBool);
    }

    /**
     * 删除索引
     */
    @Test
    public void deleteIndex() {
        IndexOperations indexOps = elasticsearchTemplate.indexOps(Product.class);
        boolean bool = indexOps.delete();
        System.out.println(bool);
    }


    /**
     * 创建文档
     */
    @Test
    public void createDoc() {
        Product product = new Product(1001L, "小米手机", "手机", 2099.0, "http://www.atguigu/hw.jpg");
        Product save = elasticsearchTemplate.save(product);
        System.out.println(save);
    }

    /**
     * 批量创建文档
     */
    @Test
    public void createBatchDoc() {
        List<Product> productList = new ArrayList<>();
        productList.add(new Product(1002L, "小米手环", "手环", 399.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1003L, "红米手机", "手机", 1999.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1004L, "华为手机", "手机", 6999.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1005L, "华为手环", "手环", 599.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1006L, "苹果手机", "手机", 6999.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1007L, "苹果手环", "手环", 2099.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1008L, "三星电视", "电视", 10999.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1009L, "小米电视", "电视", 7999.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1010L, "苹果耳机", "耳机", 2099.0, "http://www.atguigu/hw.jpg"));
        productList.add(new Product(1011L, "华为耳机", "耳机", 1999.0, "http://www.atguigu/hw.jpg"));
        Iterable<Product> iterable = elasticsearchTemplate.save(productList);
        System.out.println(iterable);
    }

    /**
     * 全量更新文档
     */
    @Test
    public void updateDoc1() {
        Product product = new Product(1001L, "小米手机", "手机", 7999.0, "http://www.baidu/hw.jpg");
        Product save = elasticsearchTemplate.save(product);
        System.out.println(save);
    }

    /**
     * 部分更新文档
     */
    @Test
    public void updateDoc2() {
        // ctx._source 固定写法
        String script = "ctx._source.price=1999.0;ctx._source.images='http://www.hhh/hw.jpg'";
        UpdateQuery updateQuery = UpdateQuery.builder("1001").withScript(script).build();
        IndexCoordinates shopping = IndexCoordinates.of("shopping");
        UpdateResponse update = elasticsearchTemplate.update(updateQuery, shopping);
        System.out.println(update);
    }

    /**
     * 根据id删除文档
     */
    @Test
    public void deleteDoc() {
        // 删除
        elasticsearchTemplate.delete("1001", Product.class);
    }

    /**
     * 根据条件删除文档
     */
    @Test
    public void deleteConditionDoc() {
        // 构建查询条件
//        QueryStringQueryBuilder stringQuery = QueryBuilders.queryStringQuery("小米手机");
        TermQueryBuilder queryBuilder = QueryBuilders.termQuery("category", "手机");
        Query query = new NativeSearchQuery(queryBuilder);
        // 删除
        ByQueryResponse response = elasticsearchTemplate.delete(query, Product.class);
        System.out.println(response.getDeleted());
    }

    /**
     * 根据id查询文档
     */
    @Test
    public void getById() {
        // 查询
        Product product = elasticsearchTemplate.get("1001", Product.class);
        System.out.println(product);
    }

    /**
     * 查询文档集合
     */
    @Test
    public void list() {
        Query query = Query.findAll();
        List<SearchHit<Product>> toList = elasticsearchTemplate.search(query, Product.class).toList();
        for (SearchHit<Product> productSearchHit : toList) {
            System.out.println(productSearchHit);
        }
    }

    /**
     * 条件查询文档集合-1
     * {
     *  "query": {
     *      "term": {   // 精确查询条件
     *          "category": {
     *              "value": "手环"
     *          }
     *      }
     *  }
     * }
     */
    @Test
    public void listByCondition1() {
        // 构建精确查询条件
        TermQueryBuilder termQuery = QueryBuilders.termQuery("category", "手环");
        NativeSearchQuery searchQuery = new NativeSearchQuery(termQuery);
        // 条件查询
        List<SearchHit<Product>> toList = elasticsearchTemplate.search(searchQuery, Product.class).toList();
        // 输出结果
        for (SearchHit<Product> productSearchHit : toList) {
            System.out.println(productSearchHit);
        }
    }

    /**
     * 条件查询文档集合-2
     * {
     *     "query": {
     *         "match": {   // 分词模糊查询
     *             "title": "米"
     *         }
     *     }
     * }
     */
    @Test
    public void listByCondition2() {
        // 构建分词查询条件
        MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("title", "米");
        NativeSearchQuery searchQuery = new NativeSearchQuery(matchQuery);
        // 条件查询
        List<SearchHit<Product>> toList = elasticsearchTemplate.search(searchQuery, Product.class).toList();
        // 输出结果
        for (SearchHit<Product> productSearchHit : toList) {
            System.out.println(productSearchHit);
        }
    }

    /**
     * 条件查询文档集合-3
     * {
     *     "query": {
     *          "filter":{ // 数据过滤
     *              "range":{
     *                  "price":{
     *                      "gt":3099.0,
     *                      "lt":7999.0
     *                  }
     *              }
     *           }
     *      }
     * }
     */
    @Test
    public void listByCondition3() {
        // 构建范围查询条件
        RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price").gte(3099.0).lte(7999.0);
        NativeSearchQuery searchQuery = new NativeSearchQuery(rangeQuery);
        // 条件查询
        List<SearchHit<Product>> toList = elasticsearchTemplate.search(searchQuery, Product.class).toList();
        // 输出结果
        for (SearchHit<Product> productSearchHit : toList) {
            System.out.println(productSearchHit);
        }
    }

    /**
     * 条件查询文档集合-3
     {
     "query": {
        "bool": {
        // "must": [ // and
            "should": [  // or
        {
            "match": { // 模糊匹配
                "category": "小米"
            }
        },
         {
            "match_phrase": { // 完全匹配
            "title": "魅族手机"
            }
        }
        ],
        "filter":{ // 数据过滤
            "range":{
                "price":{
                    "gt": 1099.0,
                    "lt": 5999.0
                    }
            }
        }
     }
     },
     "from": 0, // 页码
     "size": 5, // 页尺寸
     "_source": [ // 显示字段
         "title",
         "category",
         "price"
     ],
     "sort": { // 排序规则
        "price": {
        "order": "desc" // 从大到小
        }
     }
     }
     */
    @Test
    public void listByCondition4() {
        // 1、创建一个布尔查询构建器
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 2、构建查询条件集合
        List<QueryBuilder> queryList = new ArrayList<>();
        queryList.add(QueryBuilders.matchQuery("category","手环"));                     // 创建一个 MatchQuery 对象，用于模糊匹配 "category" 字段
        queryList.add(QueryBuilders.matchPhraseQuery("title","三星电视"));               // 创建一个 MatchPhraseQuery 对象，用于精确匹配 "title" 字段
        queryList.add(QueryBuilders.rangeQuery("price").gte(3099.0).lte(7000.0));  // 创建一个 RangeQuery 对象，用于范围过滤 "price" 字段
        // 3、逻辑与 and
//        boolQuery.must().addAll(queryList);
        // 3、逻辑或 or
        boolQuery.should().addAll(queryList);
        // 4、 创建一个 NativeSearchQuery 对象
        NativeSearchQuery searchQuery = new NativeSearchQuery(boolQuery);
        // 5、排序
        searchQuery.addSort(Sort.by(Sort.Direction.DESC,"price"));
        // 6、设置要显示的字段（两个参数分别对应包含和不包含）
        SourceFilter filter = new FetchSourceFilter(new String[]{"title","category","price"},new String[]{"images"});
        searchQuery.addSourceFilter(filter);
        // 7、设置高亮字段
        HighlightParameters highlightParameters = HighlightParameters.builder().withPreTags(new String[]{"<strong>"}).withPostTags(new String[]{"</strong>"}).build();
        HighlightQuery highlightQuery = new HighlightQuery(new Highlight(highlightParameters, Collections.singletonList(new HighlightField("category"))),null);
        searchQuery.setHighlightQuery(highlightQuery);
        // 8、设置聚合函数（度量）
//        searchQuery.addAggregation(AggregationBuilders.count("countId").field("_id"));                      // 计数
//        searchQuery.addAggregation(AggregationBuilders.avg("avgResult").field("price"));                    // 计算平均值
        // 8、设置聚合函数（词条桶）
        searchQuery.addAggregation(AggregationBuilders.terms("groupByCategory").field("category"));     // 每个category一个桶
        // 9、分页
        searchQuery.setPageable(PageRequest.of(0, 5));
        // 10、执行查询
        SearchHits<Product> search = elasticsearchTemplate.search(searchQuery, Product.class, IndexCoordinates.of("shopping"));
        // 11、输出查询结果
        for (SearchHit<Product> productSearchHit : search.toList()) {
            System.out.println(productSearchHit);
        }
        // 12、输出聚合结果
//        Aggregations agg = (Aggregations) search.getAggregations().aggregations();
//        Aggregation countId = agg.get("avgResult");
//        ParsedAvg valueAvg = (ParsedAvg) countId;
//        System.out.println(valueAvg.getValue());            // 计算平均值

        // 12、输出聚合结果
        Aggregations agg = (Aggregations) search.getAggregations().aggregations();
        Terms groupByFieldAggregation = agg.get("groupByCategory");
        List<? extends Terms.Bucket> buckets = groupByFieldAggregation.getBuckets();
        // 处理聚合结果
        for (Terms.Bucket bucket : buckets) {
            String key = bucket.getKeyAsString(); // 分组字段的值
            long docCount = bucket.getDocCount(); // 对应的文档数量
            // 处理分组数据
            System.out.println(key+" : "+docCount);
        }
    }
}
